package com.chat.handler;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chat.domain.pojo.Message;
import com.chat.service.UserService;
import com.chat.util.CommonUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wb
 * @since 2024-08-13
 */

@Slf4j
public class ChatWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 存储已经登录用户的channel对象
     */
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 存储用户id和用户的channelId绑定
     */
    public static ConcurrentHashMap<Integer, ChannelId> userMap = new ConcurrentHashMap<>();
    /**
     * 用于存储群聊房间号和群聊成员的channel信息
     */
    public static ConcurrentHashMap<Integer, ChannelGroup> groupMap = new ConcurrentHashMap<>();
    /**
     * 存储群聊房间号和群成员ID列表
     */
    public static ConcurrentHashMap<Integer, Set<Integer>> groupMemberMap = new ConcurrentHashMap<>();

    /**
     * 用户服务
     */
    private final UserService usersService = SpringUtil.getBean(UserService.class);

    private final StringRedisTemplate redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("与客户端建立连接，通道开启！");

        // 添加到 channelGroup 中
        channelGroup.add(ctx.channel());
        ctx.channel().id();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("与客户端断开连接，通道关闭！");

        // 移除用户ID和 channelId 的绑定
        channelGroup.remove(ctx.channel());

        //-------------
       for (Integer userId : userMap.keySet()) {
            if (userMap.get(userId).equals(ctx.channel().id())) {
                userMap.remove(userId);
                break;
            }
        }
        System.out.println(userMap.size()+"----userMap");
    }


   @Override
   public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

       // 首次连接是FullHttpRequest，把用户id和对应的channel对象存储起来
       if (msg instanceof FullHttpRequest) {
           FullHttpRequest request = (FullHttpRequest) msg;
           // 获取 url 中的参数
           String uri = request.uri();
           Integer userId = CommonUtil.getUrlParams(uri);

           if (userId != null) {
               // 存储用户ID和 channelId 的绑定
               userMap.put(userId, ctx.channel().id());
               log.info("登录的用户id是：{}", userId);

               // 第1次登录,获取用户加入的群聊加入到 群聊组 中
               List<Integer> groupIds = usersService.getGroupIdsById(userId);
               ChannelGroup cGroup;

               // 查询用户拥有的组是否已经创建了
               for (Integer groupId : groupIds) {
                   cGroup = groupMap.get(groupId);
                   //如果群聊管理对象没有创建
                   if (cGroup == null) {
                       //构建一个channelGroup群聊管理对象然后放入groupMap中
                       cGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
                       groupMap.put(groupId, cGroup);
                   }
                   // 把用户放到群聊管理对象里去
                   cGroup.add(ctx.channel());
                   groupMemberMap.computeIfAbsent(groupId, k -> new HashSet<>()).add(userId);
               }
               // 更新群成员列表



               // 如果url包含参数，需要处理
               if (uri.contains("?")) {
                   String newUri = uri.substring(0, uri.indexOf("?"));
                   request.setUri(newUri);
               }
           }
       } else if (msg instanceof TextWebSocketFrame) {
           TextWebSocketFrame frame = (TextWebSocketFrame) msg;
           //正常的TEXT消息类型
           log.info("客户端收到服务器数据：{}", frame.text());
           Message message = JSON.parseObject(frame.text(), Message.class);
      /*     // 处理重置未读消息数量的消息
           if ("RESET_UNREAD_COUNT".equals(message.getType())) {
               Integer chatId = message.getReceiverId();
               boolean isGroup = message.getChatId().contains("group");
               resetUnreadCount(message.getSenderId(), chatId, isGroup);
               return;
           }*/
           // 处理群聊消息
           if (message.getChatId().contains("group")) {
               System.out.println("群聊消息: " + message);
               System.out.println(groupMap);
               //--------------------------以下是新增--------------

               Integer groupId = message.getReceiverId();
               Set<Integer> groupMembers = groupMemberMap.get(groupId);
               System.out.println("groupMembers"+groupMembers);
               if (groupMembers != null) {
                   for (Integer receiverId : groupMembers) {
                       if (receiverId.equals(message.getSendId())) {
                           continue;
                       }
                       if (!userMap.containsKey(receiverId)) {
                           // 用户不在线，增加未读消息数量
                           message.setRead(false);
                           incrementUnreadCount(receiverId, groupId, true);
                       } else {
                           ChannelId channelId = userMap.get(receiverId);
                           Channel channel = channelGroup.find(channelId);
                           if (channel != null) {
                               System.out.println(receiverId+"服务端发送数据："+JSONObject.toJSONString(message));
                               channel.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(message)));
                               // 标记消息为已读
                               message.setRead(true);

                           }
                       }
                   }
               }
               // 推送群聊信息
        /*       ChannelGroup group = groupMap.get(message.getReceiverId());
               if (group != null) {
                   for (Channel channel : group) {

                       Integer receiverId = getUserIdByChannelId(channel.id());
                       System.out.println(receiverId+"------");
                       if (receiverId != null) {
                           if (receiverId.equals(message.getSendId())) {
                               // 自己发送的消息，不推送
                               continue;
                           }
                           if (!userMap.containsKey(receiverId)) {
                               // 用户不在线，增加未读消息数量
                               message.setRead(false);
                               incrementUnreadCount(receiverId, message.getReceiverId(), true);
                           } else {
                               channel.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(message)));
                               // 标记消息为已读
                               message.setRead(true);
                           }
                       }
                   }
               }*/
           }
           // 处理好友消息
           else {
               // 处理私聊的任务，如果对方也在线,则推送消息
               ChannelId channelId = userMap.get(message.getReceiverId());
               if (channelId != null) {
                   // 获取对应的channel
                   Channel ct = channelGroup.find(channelId);
                   if (ct != null) {
                       System.out.println("好友消息: " + message);
                       // 标记消息为已读
                       message.setRead(true);
                       ct.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(message)));
                   }
               }
               else {
                   // 标记消息为已读
                   message.setRead(false);
                   // 新增用户不在线，增加未读消息数量
                   incrementUnreadCount(message.getReceiverId(), message.getSendId(), false);
               }
           }

           // 将聊天信息存储到 redis 并设置过期时间为 30 天
           usersService.saveMessage(message);
       }
       System.out.println(userMap.size()+"----userMap----");
       super.channelRead(ctx, msg);
   }
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {

    }


    /**
     * 根据 ChannelId 获取用户 ID
     *
     * @param channelId ChannelId
     * @return 用户 ID
     */
    private Integer getUserIdByChannelId(ChannelId channelId) {
        for (Integer userId : userMap.keySet()) {
            if (userMap.get(userId).equals(channelId)) {
                return userId;
            }
        }
        return null;
    }

    /**
     * 增加未读消息数量
     *
     * @param receiverId 接收者 ID
     * @param chatId     聊天 ID
     * @param isGroup    是否是群聊
     */
    private void incrementUnreadCount(Integer receiverId, Integer chatId, boolean isGroup) {
        String key = getUnreadCountKey(receiverId, chatId, isGroup);
        redisTemplate.opsForValue().increment(key);
    }

    /**
     * 重置未读消息数量
     *
     * @param userId 用户 ID
     */
    private void resetUnreadCount(Integer userId) {
        // 清空私聊未读消息数量
        for (Integer senderId : userMap.keySet()) {
            String key = getUnreadCountKey(userId, senderId, false);
            redisTemplate.delete(key);
        }
        // 清空群聊未读消息数量
        for (Integer groupId : groupMap.keySet()) {
            String key = getUnreadCountKey(userId, groupId, true);
            redisTemplate.delete(key);
        }
    }

    /**
     * 获取未读消息数量的 Redis key
     *
     * @param receiverId 接收者 ID
     * @param chatId     聊天 ID
     * @param isGroup    是否是群聊
     * @return Redis key
     */
    private String getUnreadCountKey(Integer receiverId, Integer chatId, boolean isGroup) {
        return isGroup ? "unread:group:" + receiverId + ":" + chatId : "unread:private:" + receiverId + ":" + chatId;
    }
}
